package com.fary.converters;

import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.util.NumberUtils;

import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Fary
 * @version 1.0
 * @description: TODO
 * @date 2022/4/9 17:15
 */
public class TestConversionService {

    public static void main(String[] args) throws NoSuchFieldException {
        //  以下为测试类
        GenericConversionService service = new GenericConversionService();

        // 将 String 类型的时间转换为 util.Date
        service.addConverter(new Converter<String, Date>() {
            @Override
            public Date convert(String source) {
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                try {
                    return format.parse(source);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });

        // 将 Integer 转换为 String,Double,Boolean
        service.addConverter(new GenericConverter() {
            @Override
            public Set<ConvertiblePair> getConvertibleTypes() {
                return new HashSet<ConvertiblePair>() {{
                    add(new ConvertiblePair(Integer.class, String.class));
                    add(new ConvertiblePair(Integer.class, Double.class));
                    add(new ConvertiblePair(Integer.class, Boolean.class));
                }};
            }

            @Override
            public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
                Class<?> type = targetType.getType();
                if (String.class.equals(type)) {
                    return source.toString();
                } else if (Double.class.equals(type)) {
                    return Double.valueOf(source.toString());
                } else if (Boolean.class.equals(type)) {
                    return Boolean.valueOf(source.toString());
                }
                return null;
            }
        });
        // String 转 Boolean
        service.addConverter(String.class, Boolean.class, new Converter<String, Boolean>() {
            @Override
            public Boolean convert(String source) {
                return Boolean.parseBoolean(source);
            }
        });
        // String 转 Integer,Long
        service.addConverterFactory(new ConverterFactory<String, Number>() {
            @Override
            public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
                if (targetType.getTypeName().equals(Integer.class.getTypeName())) {
                    return new Converter<String, T>() {
                        @Override
                        public T convert(String source) {
                            return (T) NumberUtils.parseNumber(source, Integer.class);
                        }
                    };
                } else if (targetType.getTypeName().equals(Long.class.getTypeName())) {
                    return new Converter<String, T>() {
                        @Override
                        public T convert(String source) {
                            return (T) NumberUtils.parseNumber(source, Long.class);
                        }
                    };
                }
                return null;
            }
        });
        service.addConverter(new ConditionalGenericConverter() {
            @Override
            public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
                if (sourceType.getAnnotation(Resource.class) != null) {
                    return true;
                }
                return false;
            }

            /**
             * 告诉转换器总部，我这个转换器支持什么样的转换，可以支持多个转换
             * 此处支持 string -> list 和 string -> map
             */
            @Override
            public Set<ConvertiblePair> getConvertibleTypes() {
                Set<ConvertiblePair> convertiblePairs = Collections.newSetFromMap(new ConcurrentHashMap<>());
                ConvertiblePair stringToArray = new ConvertiblePair(String.class, List.class);
                ConvertiblePair stringToMap = new ConvertiblePair(String.class, Map.class);
                convertiblePairs.add(stringToArray);
                convertiblePairs.add(stringToMap);
                return convertiblePairs;
            }

            /**
             * @param source     被转换的东西
             * @param sourceType 被转换的东西的上下文，可以用于设置条件，具体咋玩看typeDescriptor，比如可以用来判断转换源是否拥有某些注解
             * @param targetType 转换目标类型的上下文
             * @return 案例场景：如果转换源被@Resource标识，并且目标类型为List，则转换list
             * 目标类型为map，则转换为map
             * @see TypeDescriptor
             */
            @Override
            public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
                if (targetType.getType() == List.class) {
                    return Arrays.asList(source.toString().split(":"));
                }
                if (targetType.getType() == Map.class) {
                    Map<String, String> map = new HashMap<>();
                    map.put("a", source.toString().split(":")[0]);
                    map.put("b", source.toString().split(":")[1]);
                    return map;
                }
                return null;
            }
        });

        boolean checkTimeToDate = service.canConvert(Map.class, List.class);
        System.out.println("Map能否转换为List：" + checkTimeToDate);
        Date targetDate = service.convert("2021-12-20 13:01:01", Date.class);
        System.out.println(targetDate);
        String targetString = service.convert(1, String.class);
        System.out.println(targetString);
        Double targetDouble = service.convert(1, Double.class);
        System.out.println(targetDouble);
        Boolean targetBoolean = service.convert(1, Boolean.class);
        System.out.println(targetBoolean);
        Boolean stringToBoolean = service.convert("true", Boolean.class);
        System.out.println(stringToBoolean);
        Integer stringToInteger = service.convert("200", Integer.class);
        System.out.println(stringToInteger);
        Long stringToLong = service.convert("300", Long.class);
        System.out.println(stringToLong);
        Object list = service.convert(new Apple("1:2").getName(),new TypeDescriptor(Apple.class.getDeclaredField("name")), TypeDescriptor.valueOf(List.class));
        System.out.println(list);
    }
}
